package powerga.transmission.minloss;

import org.apache.commons.math.complex.Complex;
import org.gridgain.grid.gridify.Gridify;
import org.interpss.PluginObjectFactory;
import org.interpss.custom.IpssFileAdapter;
import org.jenetics.FitnessFunction;
import org.jenetics.Genotype;
import org.jenetics.Integer64Gene;

import com.interpss.common.datatype.UnitType;
import com.interpss.common.exp.InterpssException;
import com.interpss.core.CoreObjectFactory;
import com.interpss.core.aclf.AclfBranch;
import com.interpss.core.aclf.AclfBus;
import com.interpss.core.aclf.AclfNetwork;
import com.interpss.core.algo.LoadflowAlgorithm;
import com.interpss.core.net.Branch;
import com.interpss.core.net.Bus;

public class SimplePLossFunction implements	FitnessFunction<Integer64Gene, Double> {

	/**
	 * 
	 */
	private static final long serialVersionUID = -4664033119538403211L;
	
	private AclfNetwork net;
	private int loadNumber;
	private int transNumber;
	private int maxCapNumber;	// Maximum capacitor number for a single bus
	private int maxRatioSteps;	// Maximum ratio steps for a single OLTC

	public SimplePLossFunction(AclfNetwork net, int loadNumber, int transNumber, int maxCapNumber, int maxRatioSteps) {
		super();
		this.net = net;
		this.loadNumber = loadNumber;
		this.transNumber = transNumber;
		this.maxCapNumber = maxCapNumber;
		this.maxRatioSteps = maxRatioSteps;
	}

	// Computing the value of the fitness function
	@Gridify
	@Override
	public Double evaluate(Genotype<Integer64Gene> genotype) {
		// 1. Initializing
		// 1.1. Retrieve the genes corresponding to capacitors from the chromosome
		int[] capSites = new int[loadNumber];
		for (int i = 0; i < loadNumber; i++)
			capSites[i] = genotype.getChromosome().getGene(i).intValue();
		// 1.2. Retrieve the genes corresponding to OLTC ratios from the chromosome
		int[] transSites = new int[transNumber];
		for (int i = 0; i < transNumber; i++)
			transSites[i] = genotype.getChromosome().getGene(i + loadNumber).intValue();
		try {
			// 2. Construct the new network by the information of chromosome
			// 2.1. Clone the original network
//			AclfNetwork newNet = CoreObjectFactory.createAclfNetwork(net.serialize());
			AclfNetwork newNet = PluginObjectFactory.getFileAdapter(IpssFileAdapter.FileFormat.IEEECDF).load("testData/ieee14.ieee").getAclfNet();
			// 2.2. Deal with capacitors
			int i = 0;
			for (Bus thisBus : newNet.getBusList()) {
				AclfBus thisAclfBus = newNet.getAclfBus(thisBus.getId());
				if (thisAclfBus.isLoad()) {
					// Value of the gene site can be 0 ~ (loadNumber + transNumber - 1)
					int capInOperation = capSites[i] % maxCapNumber;
					Complex thisShuntY = thisAclfBus.getShuntY();
					// Calculate corresponding shunt Y
					// Q=Vb*Vb*B, thus Q=B for per unit value
					// 0.01 corresponds to 10MVar
					thisAclfBus.setShuntY(thisShuntY.add(new Complex(0.0, 0.01 * capInOperation)));
					i++;
				}
			}
			// 2.3. Deal with transformers
			i = 0;
			for (Branch thisBranch : newNet.getBranchList()) {
				AclfBranch thisAclfBranch = newNet.getAclfBranch(thisBranch.getId());
				if (thisAclfBranch.isXfr()) {
					int ratioInOperation = transSites[i] % maxRatioSteps;
					double thisRatio = 1.0 + (ratioInOperation - (maxRatioSteps - 1) / 2) * 0.025;
					double fromVB = thisAclfBranch.getFromAclfBus().getBaseVoltage();
					double toVB = thisAclfBranch.getToAclfBus().getBaseVoltage();
					// only ratio on high-voltage side can be modified
					if (fromVB > toVB) {
						thisAclfBranch.setFromTurnRatio(thisRatio);
						thisAclfBranch.setToTurnRatio(1.0);
					}
					else {
						thisAclfBranch.setToTurnRatio(thisRatio);
						thisAclfBranch.setFromTurnRatio(1.0);
					}
					i++;
				}
			}
			// 3. Solve load flow
		  	LoadflowAlgorithm algo = CoreObjectFactory.createLoadflowAlgorithm(newNet);
		  	boolean pfSuccess = algo.loadflow();
		  	// 4. Calculate the P losses
		  	if (pfSuccess) {	// Load flow solved successfully
			  	Double loss = 0.0;
			  	for (Branch thisBranch : newNet.getBranchList()) {
			  		double thisCurrent = newNet.getAclfBranch(thisBranch.getId()).current(UnitType.Amp);
			  		double thisVB = newNet.getAclfBus(newNet.getAclfBranch(thisBranch.getId()).getFromBusId()).getBaseVoltage() / 
			  				1000.0;
			  		double thisSB = newNet.getBaseKva() / 1000.0;
			  		double thisR = newNet.getAclfBranch(thisBranch.getId()).getZ().getReal() * thisVB * thisVB / thisSB;
			  		loss += thisCurrent * thisCurrent * thisR;
			  	}
			  	loss /= 1000.0;
//				System.out.println("Totally active power amount is " + loss + "kW.");
				return loss;
		  	}
		  	else {	// Failed to solve load flow
				return 1.0E10;
		  	}
		} catch (InterpssException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return 1.0E+10;
	}

}